﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;

namespace Waaahsabi.Controls.DWM
{
	public class TheGameWindow : ContentControl
	{
		public event EventHandler<RoutedEventArgs> OnClosing;
		public event EventHandler<RoutedEventArgs> OnClosed;
		public event EventHandler<RoutedEventArgs> OnFocus;
		public event EventHandler<RoutedEventArgs> OnMinimize;
		public event EventHandler<RoutedEventArgs> OnMaximize;
		public event EventHandler<RoutedEventArgs> OnShowRequested;

		public UIElementCollection Children { get; set; }
		private Brush _windowBackground;
		public Brush WindowBackground
		{
			get
			{
				return _windowBackground;
			}
			set
			{
				_windowBackground = value;
			}
		}

		public WindowState WindowState { get; private set; }
		
		#region local controls
		private Border _bdrTopMiddle;
		private Border _bdrMiddleLeft;
		private Border _bdrMiddleRight;
		private Border _bdrBottomMiddle;
		private Border _bdrBottomRight;
		private Border _bdrBottomLeft;
		private Border _bdrCenter;
		private Button _btnMinimize;
		private Button _btnMaximize;
		private Button _btnClose;

		private Grid _shadowGrid;
		#endregion

		#region public properties
		public bool AllowClose { get; set; }
		public bool AllowMinimize { get; set; }
		public bool AllowMaximize { get; set; }
		public bool IsFocused { get; internal set; }

		#region WindowTitle DependencyProperty
		public static readonly DependencyProperty WindowTitleProperty =
		DependencyProperty.Register
			(
			"WindowTitle",
			typeof(string),
			typeof(TheGameWindow),
			new PropertyMetadata("Window title")
			);

		public string WindowTitle
		{
			get { return (string)GetValue(WindowTitleProperty); }
			set { SetValue(WindowTitleProperty, value); }
		}
		#endregion

		#endregion

		private Effect _originalEffect;
		private UIElement _parent;
		private Size _normalSize;
		private Point _normalPosition;
		private readonly Guid _windowId;
		public Guid WindowId
		{
			get { return _windowId; }
		}

		public TheGameWindow()
		{
			DefaultStyleKey = typeof (TheGameWindow);
		}

		public TheGameWindow(Guid id)
		{
			DefaultStyleKey = typeof(TheGameWindow);
			_windowId = id;
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();
			if (Parent != null)
				_parent = Parent as UIElement;

			_shadowGrid = GetTemplateChild("shadowGrid") as Grid;
			_bdrCenter = GetTemplateChild("bdrCenter") as Border;
			if(_bdrCenter != null)
			{
				_bdrCenter.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
				};
			}
			#region dragging
			_bdrTopMiddle = GetTemplateChild("bdrTopMiddle") as Border;
			if (_bdrTopMiddle != null)
			{
				bool isDragging = false;
				Point clickPoint = new Point();
				Point windowPos = new Point();
				_bdrTopMiddle.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
					isDragging = true;
					clickPoint = e.GetPosition(null);
					_bdrTopMiddle.CaptureMouse();
					windowPos = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
				};

				_bdrTopMiddle.MouseLeftButtonUp += (s, e) =>
				{
					isDragging = false;
					_bdrTopMiddle.ReleaseMouseCapture();
					_normalPosition = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
				};

				_bdrTopMiddle.MouseMove += (s, e) =>
				{
					if (!isDragging)
						return;
					Point currentMousePos = e.GetPosition(null);
					Point delta = new Point(currentMousePos.X - clickPoint.X, currentMousePos.Y - clickPoint.Y);
					Canvas.SetLeft(this, windowPos.X + delta.X);
					Canvas.SetTop(this, windowPos.Y + delta.Y);
				};
			}

			#endregion

			#region window buttons
			_btnMinimize = GetTemplateChild("btnMinimize") as Button;
			if(_btnMinimize != null)
			{
				if(!AllowMinimize)
					_btnMinimize.IsEnabled = false;
				else
					_btnMinimize.IsEnabled = true;
				
				_btnMinimize.Click += (s, e) => Minimize();
			}

			_btnMaximize = GetTemplateChild("btnMaximize") as Button;
			if(_btnMaximize != null)
			{
				if(!AllowMaximize)
					_btnMaximize.IsEnabled = false;
				else
					_btnMaximize.IsEnabled = true;
				_btnMaximize.Click += (s, e) =>
				{
					switch (WindowState)
					{
						case WindowState.Maximized:
							//restore to normal size and position
							Width = _normalSize.Width;
							Height = _normalSize.Height;
							Canvas.SetLeft(this, _normalPosition.X);
							Canvas.SetTop(this, _normalPosition.Y);
							WindowState = WindowState.Normal;
							_btnMaximize.Content = "1";
							_shadowGrid.Effect = _originalEffect;
							break;
						case WindowState.Normal:
							//restore to maximum size (parent's extends)
							_normalSize = RenderSize;
							_normalPosition = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
							WindowState = WindowState.Maximized;
							Width = _parent.RenderSize.Width;
							Height = _parent.RenderSize.Height;
							Canvas.SetLeft(this, 0);
							Canvas.SetTop(this, 0);
							_btnMaximize.Content = "2";
							_originalEffect = _shadowGrid.Effect;
							_shadowGrid.Effect = null;
							break;
						default:
							return;
					}
					if (OnMaximize != null)
						OnMaximize(this, new RoutedEventArgs());
				};
			}
			_btnClose = GetTemplateChild("btnClose") as Button;
			if (_btnClose != null)
			{
				if (!AllowClose)
					_btnClose.IsEnabled = false;
				else
					_btnClose.IsEnabled = true;

				_btnClose.Click += (s, e) =>
				{
					if (OnClosing != null) OnClosing(this, new RoutedEventArgs()); // receiver can do something before OnClosed (likely the WM will do some cleaning up of it's structure)
					if (OnClosed != null) OnClosed(this, new RoutedEventArgs()); // to be used by window instantiator mostly
				};
			}
			#endregion

			#region Sizing
			double originalWidth = Width;
			double originalHeight = Height;

			#region right
			_bdrMiddleRight = GetTemplateChild("bdrMiddleRight") as Border;
			if(_bdrMiddleRight != null)
			{
				Point clickPoint = new Point();
				bool isResizing = false;
				_bdrMiddleRight.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
					if (WindowState == WindowState.Maximized) return;
					isResizing = true;
					clickPoint = e.GetPosition(null);
					_bdrMiddleRight.CaptureMouse();
				};

				_bdrMiddleRight.MouseLeftButtonUp += (s, e) =>
				{
					isResizing = false;
					_bdrMiddleRight.ReleaseMouseCapture();
					originalWidth = Width;
					_normalSize = RenderSize;
				};

				_bdrMiddleRight.MouseMove += (s, e) =>
				{
					if(!isResizing) return;
					Point currPos = e.GetPosition(null);
					double width = originalWidth + (currPos.X - clickPoint.X);
					if(width > 200)
						Width = width;
				};
			}
			#endregion

			#region left
			_bdrMiddleLeft = GetTemplateChild("bdrMiddleLeft") as Border;
			if (_bdrMiddleLeft != null)
			{
				Point clickPoint = new Point();
				bool isResizing = false;
				_bdrMiddleLeft.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
					if (WindowState == WindowState.Maximized) return;
					isResizing = true;
					clickPoint = e.GetPosition(null);
					_bdrMiddleLeft.CaptureMouse();
				};

				_bdrMiddleLeft.MouseLeftButtonUp += (s, e) =>
				{
					isResizing = false;
					_bdrMiddleLeft.ReleaseMouseCapture();
					originalWidth = Width;
					_normalPosition = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
					_normalSize = RenderSize;
				};

				_bdrMiddleLeft.MouseMove += (s, e) =>
				{
					if (!isResizing) return;
					Point currPos = e.GetPosition(null);
					double width = originalWidth + (clickPoint.X - currPos.X);
					if (width > 200)
					{
						Width = width;
						double delta = originalWidth - width;
						Canvas.SetLeft(this, _normalPosition.X + (delta));
					}
				};
			}
			#endregion

			#region Bottom
			_bdrBottomMiddle = GetTemplateChild("bdrBottomMiddle") as Border;
			if (_bdrMiddleRight != null)
			{
				Point clickPoint = new Point();
				bool isResizing = false;
				_bdrBottomMiddle.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
					if (WindowState == WindowState.Maximized) return;
					isResizing = true;
					clickPoint = e.GetPosition(null);
					_bdrBottomMiddle.CaptureMouse();
				};

				_bdrBottomMiddle.MouseLeftButtonUp += (s, e) =>
				{
					isResizing = false;
					_bdrBottomMiddle.ReleaseMouseCapture();
					originalHeight = Height;
					_normalSize = RenderSize;
				};

				_bdrBottomMiddle.MouseMove += (s, e) =>
				{
					if (!isResizing) return;
					Point currPos = e.GetPosition(null);
					double height = originalHeight + (currPos.Y - clickPoint.Y);
					if (height > 200)
						Height = height;
				};
			}
			#endregion

			#region BottomRight
			_bdrBottomRight = GetTemplateChild("bdrBottomRight") as Border;
			if (_bdrBottomRight != null)
			{
				_bdrBottomRight.Cursor = Cursors.SizeNWSE;
				Point clickPoint = new Point();
				bool isResizing = false;
				_bdrBottomRight.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
					if (WindowState == WindowState.Maximized) return;
					isResizing = true;
					clickPoint = e.GetPosition(null);
					_bdrBottomRight.CaptureMouse();
				};

				_bdrBottomRight.MouseLeftButtonUp += (s, e) =>
				{
					isResizing = false;
					_bdrBottomRight.ReleaseMouseCapture();
					originalHeight = Height;
					originalWidth = Width;
					_normalSize = RenderSize;
				};

				_bdrBottomRight.MouseMove += (s, e) =>
				{
					if (!isResizing) return;
					Point currPos = e.GetPosition(null);
					double width = originalWidth + (currPos.X - clickPoint.X);
					double height = originalHeight + (currPos.Y - clickPoint.Y);
					if (height > 200)
						Height = height;
					if (width > 200)
						Width = width;
				};
			}
			#endregion

			#region BottomLeft
			_bdrBottomLeft = GetTemplateChild("bdrBottomLeft") as Border;
			if (_bdrBottomLeft != null)
			{
				_bdrBottomLeft.Cursor = Cursors.SizeNESW;
				Point clickPoint = new Point();
				bool isResizing = false;
				_bdrBottomLeft.MouseLeftButtonDown += (s, e) =>
				{
					if (OnFocus != null)
						OnFocus(this, e);
					if (WindowState == WindowState.Maximized) return;
					isResizing = true;
					clickPoint = e.GetPosition(null);
					_bdrBottomLeft.CaptureMouse();
				};

				_bdrBottomLeft.MouseLeftButtonUp += (s, e) =>
				{
					isResizing = false;
					_bdrBottomLeft.ReleaseMouseCapture();
					originalHeight = Height;
					originalWidth = Width;
					_normalPosition = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
					_normalSize = RenderSize;
				};

				_bdrBottomLeft.MouseMove += (s, e) =>
				{
					if (!isResizing) return;
					Point currPos = e.GetPosition(null);
					double width = originalWidth + (clickPoint.X - currPos.X);
					double height = originalHeight + (currPos.Y - clickPoint.Y);

					if (height > 200)
						Height = height;

					if (width > 200)
					{
						Width = width;
						double delta = originalWidth - width;
						Canvas.SetLeft(this, _normalPosition.X + (delta));
					}
				};
			}
			#endregion
			#endregion
		}

		public void Close()
		{
			if (OnClosing != null) OnClosing(this, new RoutedEventArgs()); // receiver can do something before OnClosed (likely the WM will do some cleaning up of it's structure)
			if (OnClosed != null) OnClosed(this, new RoutedEventArgs()); // to be used by window instantiator mostly
		}
		
		/// <summary>
		/// Closes the window normally, but without firing the OnClosing event. Use with caution and proper deinitializing!
		/// </summary>
		public void CloseInstantly()
		{
			if(OnClosed != null) OnClosed(this, new RoutedEventArgs());
		}

		public void Show()
		{
			if (OnShowRequested != null)
				OnShowRequested(this, null);
		}

		public void SetSize(Size s)
		{
			Width = s.Width;
			Height = s.Height;
		}

		public void Minimize()
		{			
			Visibility = Visibility.Collapsed;
			WindowState = WindowState.Minimized;
			_normalSize = RenderSize;
			_normalPosition = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
			if (OnMinimize != null)
				OnMinimize(this, new RoutedEventArgs());
		}

		public void Restore()
		{
			WindowState = WindowState.Normal;
			Visibility = Visibility.Visible;
			Width = _normalSize.Width;
			Height = _normalSize.Height;
			Canvas.SetLeft(this, _normalPosition.X);
			Canvas.SetTop(this, _normalPosition.Y);
		}

		public new void Focus()
		{
			if (OnFocus != null)
				OnFocus(this, null);
		}

		public override string ToString()
		{
			return WindowTitle;
		}
	}

	public enum WindowState
	{
		Normal,
		Minimized,
		Maximized
	}
}
